////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2012 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h> >
#include "..\FM4.h"

/*
 *  Register Bit Definition
 */
#define SMR_SOE          0x01U
#define SMR_SCKE         0x02U
#define SMR_BDS          0x04U
#define SMR_SCINV        0x08U
#define SMR_WUCR         0x10U
#define SMR_MD_UART      0x00U
#define SMR_MD_UART_MP   0x20U
#define SMR_MD_SIO       0x40U
#define SMR_MD_LIN       0x60U
#define SMR_MD_I2C       0x80U

#define SCR_TXE          0x01U
#define SCR_RXE          0x02U
#define SCR_TBIE         0x04U
#define SCR_TIE          0x08U
#define SCR_RIE          0x10U
#define SCR_SPI          0x20U
#define SCR_MS           0x40U
#define SCR_UPCL         0x80U

#define SSR_TBI          0x01U
#define SSR_TDRE         0x02U
#define SSR_RDRF         0x04U
#define SSR_ORE          0x08U
#define SSR_FRE          0x10U
#define SSR_PE           0x20U
#define SSR_REC          0x80U

#define ESCR_P           0x08U
#define ESCR_PEN         0x10U
#define ESCR_INV         0x20U
#define ESCR_ESBL        0x40U
#define ESCR_FLWEN       0x80U
#define ESCR_DATABITS_8  0x00U
#define ESCR_DATABITS_5  0x01U
#define ESCR_DATABITS_6  0x02U
#define ESCR_DATABITS_7  0x03U
#define ESCR_DATABITS_9  0x04U

#define BGR_EXT          0x8000U

#define FCR1_FSEL        0x01U
#define FCR1_FTIE        0x02U
#define FCR1_FDRQ        0x04U
#define FCR1_FRIIE       0x08U
#define FCR1_FLSTE       0x10U

#define FCR0_FE1         0x01U
#define FCR0_FE2         0x02U
#define FCR0_FCL1        0x04U
#define FCR0_FCL2        0x08U
#define FCR0_FSET        0x10U
#define FCR0_FLD         0x20U
#define FCR0_FLST        0x40U

#define SPI_MAX    2
// pins
#define SPI1_SCLK_Pin    P07
#define SPI1_MISO_Pin    P05
#define SPI1_MOSI_Pin    P06
#define SPI2_SCLK_Pin    P38
#define SPI2_MISO_Pin    P36
#define SPI2_MOSI_Pin    P37

static FM4_MFS_TypeDef *g_csio[SPI_MAX] = {FM4_MFS7, FM4_MFS5};

/*
struct SPI_CONFIGURATION
{
    GPIO_PIN       DeviceCS;
    BOOL           CS_Active;             // False = LOW active,      TRUE = HIGH active
    BOOL           MSK_IDLE;              // False = LOW during idle, TRUE = HIGH during idle
    BOOL           MSK_SampleEdge;        // False = sample falling edge, TRUE = samples on rising
    BOOL           MD_16bits;
    UINT32         Clock_RateKHz;
    UINT32         CS_Setup_uSecs;
    UINT32         CS_Hold_uSecs;
    UINT32         SPI_mod;
    GPIO_FLAG      BusyPin;
};
*/

BOOL CPU_SPI_Initialize()
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    return TRUE;
}

void CPU_SPI_Uninitialize()
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
}

BOOL CPU_SPI_nWrite16_nRead16(const SPI_CONFIGURATION& Configuration, UINT16* Write16, INT32 WriteCount, UINT16* Read16, INT32 ReadCount, INT32 ReadStartOffset)
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    if (!CPU_SPI_Xaction_Start(Configuration))
        return FALSE;
    SPI_XACTION_16 Transaction;
    Transaction.Read16          = Read16;
    Transaction.ReadCount       = ReadCount;
    Transaction.ReadStartOffset = ReadStartOffset;
    Transaction.Write16         = Write16;
    Transaction.WriteCount      = WriteCount;
    Transaction.SPI_mod         = Configuration.SPI_mod;
    if (!CPU_SPI_Xaction_nWrite16_nRead16(Transaction))
            return FALSE;
    return CPU_SPI_Xaction_Stop(Configuration);
}

BOOL CPU_SPI_nWrite8_nRead8(const SPI_CONFIGURATION& Configuration, UINT8* Write8, INT32 WriteCount, UINT8* Read8, INT32 ReadCount, INT32 ReadStartOffset)
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    if (!CPU_SPI_Xaction_Start( Configuration ))
        return FALSE;
    SPI_XACTION_8 Transaction;
    Transaction.Read8           = Read8;
    Transaction.ReadCount       = ReadCount;
    Transaction.ReadStartOffset = ReadStartOffset;
    Transaction.Write8          = Write8;
    Transaction.WriteCount      = WriteCount;
    Transaction.SPI_mod         = Configuration.SPI_mod;
    if (!CPU_SPI_Xaction_nWrite8_nRead8(Transaction))
        return FALSE;
    return CPU_SPI_Xaction_Stop( Configuration );
}

BOOL CPU_SPI_Xaction_Start( const SPI_CONFIGURATION& Configuration )
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    UINT32 spi_mod = Configuration.SPI_mod;
    UINT8 scr = 0;
    UINT8 smr = 0;
    if (spi_mod >= SPI_MAX)
        return FALSE;
    FM4_MFS_TypeDef* spi = g_csio[spi_mod];
    if (spi_mod == 0) {
        // channel 7 - P05, P06, P07
        FM4_GPIO->EPFR08 |= 0x05400000;
        FM4_GPIO->PFR0   |= 0x000000e0;
    } else {
        // channel 5 - P36, P37, P38
        FM4_GPIO->EPFR08 |= 0x0000fc00;
        FM4_GPIO->PFR3   |= 0x000001c0;
    }
    spi->SCR = SCR_UPCL;        // Initialize
//#define SPI_FIFO
#ifdef SPI_FIFO
    spi->FBYTE2 = 0x01;         // Receive FIFO
    spi->FCR = FCR0_FCL2 |      // FIFO2 start
                FCR0_FCL1 |     // FIFO1 start
                FCR0_FE2 |      // FIFO2 enable
                FCR0_FE1;       // FIFO1 enable
#endif
    //spi->SSR |= SSR_REC;      // Clear error flag
    if (Configuration.MSK_IDLE != true)         // CPOL(Clock Polatity)
        smr = SMR_SCINV;                        // false (low) -> SCINV=1
    if (Configuration.MSK_SampleEdge == true)   // CPHA(Clock Phase)
        scr = SCR_SPI;                          // true (rising) -> SPI on
    spi->SMR = smr
                | SMR_MD_SIO    // CLK sync
                | SMR_BDS       // MBS first
                | SMR_SCKE      // CLK enable
                | SMR_SOE;      // Data output enable
    // baudrate
    // defined in platform_selector.h
    UINT16 bgr = (UINT16)((PCLK2 / (Configuration.Clock_RateKHz * 1000)) - 1);
    spi->BGR = bgr;
    spi->ESCR = (ESCR_DATABITS_8);  // 8 bit
    spi->SCR |= (scr |          // SPI mode
                SCR_RXE |       // Receive enable
                SCR_TXE);       // Transfer enable
    // CS setup
    CPU_GPIO_EnableOutputPin(Configuration.DeviceCS, Configuration.CS_Active);
    if (Configuration.CS_Setup_uSecs) {
        HAL_Time_Sleep_MicroSeconds_InterruptEnabled(Configuration.CS_Setup_uSecs);
    }
    return TRUE;
}

BOOL CPU_SPI_Xaction_Stop(const SPI_CONFIGURATION& Configuration)
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    
    if(Configuration.CS_Hold_uSecs)
    {
        HAL_Time_Sleep_MicroSeconds_InterruptEnabled(Configuration.CS_Hold_uSecs);
    }
    CPU_GPIO_SetPinState(Configuration.DeviceCS, !Configuration.CS_Active);
    return TRUE;
}

void Wait_TDRE_HI(FM4_MFS_TypeDef* spi)
{
    unsigned int timeout = 100000;
    while ((spi->SSR & SSR_TDRE) == 0) {
        if (timeout-- == 0)
            break;
    }
}


BOOL CPU_SPI_Xaction_nWrite16_nRead16(SPI_XACTION_16& Transaction)
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    UINT8 value;
    UINT32 spi_mod = Transaction.SPI_mod;
    if (spi_mod >= SPI_MAX)
        return FALSE;
    FM4_MFS_TypeDef* spi = g_csio[spi_mod];

    UINT8* wbuf = (UINT8*)Transaction.Write16;
    UINT8* rbuf = (UINT8*)Transaction.Read16;
    INT32 wcnt = Transaction.WriteCount;
    INT32 rcnt = Transaction.ReadCount;
    INT32 roff = Transaction.ReadStartOffset;
    INT32 i = 0;
    while (i < wcnt) {
        spi->TDR = *(wbuf+1);
        if (rcnt > 0) {
            while ((spi->SSR & SSR_RDRF) == 0);
            value = spi->RDR;
            if ((i >= roff) && (i < roff + rcnt)) {
                *(rbuf+1) = value;
            }
        }
        spi->TDR = *wbuf;
        if (rcnt > 0) {
            while ((spi->SSR & SSR_RDRF) == 0);
            value = spi->RDR;
            if ((i >= roff) && (i < roff + rcnt)) {
                *rbuf = value;
                rbuf += 2;
            }
        }
        wbuf += 2;
        i++;
    }
    while (i < roff + rcnt) {
        spi->TDR = 0xff;
        while ((spi->SSR & SSR_RDRF) == 0);
        value = spi->RDR;
        if ((i >= roff) && (i < roff + rcnt)) {
            *(rbuf+1) = value;
        }
        spi->TDR = 0xff;
        while ((spi->SSR & SSR_RDRF) == 0);
        value = spi->RDR;
        if ((i >= roff) && (i < roff + rcnt)) {
            *rbuf = value;
            rbuf += 2;
        }
        i++;
    }
    return TRUE;
}

BOOL CPU_SPI_Xaction_nWrite8_nRead8(SPI_XACTION_8& Transaction)
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    UINT32 spi_mod = Transaction.SPI_mod;
    if (spi_mod >= SPI_MAX)
        return FALSE;
    FM4_MFS_TypeDef* spi = g_csio[spi_mod];

    UINT8* wbuf = Transaction.Write8;
    UINT8* rbuf = Transaction.Read8;
    INT32 wcnt = Transaction.WriteCount;
    INT32 rcnt = Transaction.ReadCount;
    INT32 roff = Transaction.ReadStartOffset;
    INT32 i = 0;
    while (i < wcnt) {
        spi->TDR = *wbuf++;
        if (rcnt > 0) {
            while ((spi->SSR & SSR_RDRF) == 0);
            UINT8 value = spi->RDR;
            if ((i >= roff) && (i < roff + rcnt)) {
                *rbuf++ = value;
            }
        }
        i++;
    }
    while (i < roff + rcnt) {
        spi->TDR = 0xff;
        while ((spi->SSR & SSR_RDRF) == 0);
        UINT8 value = spi->RDR;
        if ((i >= roff) && (i < roff + rcnt)) {
            *rbuf++ = value;
        }
        i++;
    }
    return TRUE;
}

UINT32 CPU_SPI_PortsCount()
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    return SPI_MAX;
}

void CPU_SPI_GetPins(UINT32 spi_mod, GPIO_PIN& msk, GPIO_PIN& miso, GPIO_PIN& mosi)
{
    NATIVE_PROFILE_HAL_PROCESSOR_SPI();
    if (spi_mod == 0) {
        msk  = (GPIO_PIN)SPI1_SCLK_Pin;
        miso = (GPIO_PIN)SPI1_MISO_Pin;
        mosi = (GPIO_PIN)SPI1_MOSI_Pin;
    } else if (spi_mod == 1) {
        msk  = (GPIO_PIN)SPI2_SCLK_Pin;
        miso = (GPIO_PIN)SPI2_MISO_Pin;
        mosi = (GPIO_PIN)SPI2_MOSI_Pin;
    } else {
        msk  = (GPIO_PIN)0xff;
        miso = (GPIO_PIN)0xff;
        mosi = (GPIO_PIN)0xff;
    }
}
